<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><title>qiankun源码解读-框架源码篇 | Pluto</title><meta name="author" content="拾壹baby"><meta name="copyright" content="拾壹baby"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="框架源码整个框架的源码目录是 src，入口文件是 src&#x2F;index.ts 入口 src&#x2F;index.ts&#x2F;**  * 在示例或者官网提到的所有 API 都在这里统一导出  *&#x2F; &#x2F;&#x2F; 最关键的三个，手动加载微应用、基于路由配置、启动 qiankun export &amp;#123; loadMicroApp, registerMicroApps, start &amp;#125; from">
<meta property="og:type" content="article">
<meta property="og:title" content="qiankun源码解读-框架源码篇">
<meta property="og:url" content="http://cdn.pluto1811.com/pluto/2022/09/02/qiankun02/">
<meta property="og:site_name" content="Pluto">
<meta property="og:description" content="框架源码整个框架的源码目录是 src，入口文件是 src&#x2F;index.ts 入口 src&#x2F;index.ts&#x2F;**  * 在示例或者官网提到的所有 API 都在这里统一导出  *&#x2F; &#x2F;&#x2F; 最关键的三个，手动加载微应用、基于路由配置、启动 qiankun export &amp;#123; loadMicroApp, registerMicroApps, start &amp;#125; from">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://cdn.pluto1811.com/goodsImage/1615971759919/1615971739665.jpg">
<meta property="article:published_time" content="2022-09-02T02:10:55.000Z">
<meta property="article:modified_time" content="2022-11-12T03:18:34.483Z">
<meta property="article:author" content="拾壹baby">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://cdn.pluto1811.com/goodsImage/1615971759919/1615971739665.jpg"><link rel="shortcut icon" href="/pluto/img/favicon.png"><link rel="canonical" href="http://cdn.pluto1811.com/pluto/2022/09/02/qiankun02/"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/pluto/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.min.css" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = { 
  root: '/pluto/',
  algolia: undefined,
  localSearch: undefined,
  translate: undefined,
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: false,
  isAnchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: 'qiankun源码解读-框架源码篇',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2022-11-12 11:18:34'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><meta name="generator" content="Hexo 6.3.0"><link rel="alternate" href="/pluto/atom.xml" title="Pluto" type="application/atom+xml">
</head><body><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="https://i.loli.net/2021/02/24/5O1day2nriDzjSu.png" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="sidebar-site-data site-data is-center"><a href="/pluto/archives/"><div class="headline">文章</div><div class="length-num">22</div></a><a href="/pluto/tags/"><div class="headline">标签</div><div class="length-num">0</div></a><a href="/pluto/categories/"><div class="headline">分类</div><div class="length-num">0</div></a></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/pluto/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/pluto/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/pluto/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/pluto/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/pluto/link/"><i class="fa-fw fas fa-link"></i><span> 友链</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('http://cdn.pluto1811.com/goodsImage/1615971759919/1615971739665.jpg')"><nav id="nav"><span id="blog_name"><a id="site-name" href="/pluto/">Pluto</a></span><div id="menus"><div class="menus_items"><div class="menus_item"><a class="site-page" href="/pluto/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/pluto/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/pluto/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/pluto/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/pluto/link/"><i class="fa-fw fas fa-link"></i><span> 友链</span></a></div></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">qiankun源码解读-框架源码篇</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-09-02T02:10:55.000Z" title="发表于 2022-09-02 10:10:55">2022-09-02</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2022-11-12T03:18:34.483Z" title="更新于 2022-11-12 11:18:34">2022-11-12</time></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="qiankun源码解读-框架源码篇"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"><i class="fa-solid fa-spinner fa-spin"></i></span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><h1 id="框架源码"><a href="#框架源码" class="headerlink" title="框架源码"></a>框架源码</h1><p>整个框架的源码目录是 src，入口文件是 <font color="#ec5d3c" bgcolor="#fdf5f5f5">src&#x2F;index.ts</font></p>
<h4 id="入口-src-x2F-index-ts"><a href="#入口-src-x2F-index-ts" class="headerlink" title="入口 src&#x2F;index.ts"></a>入口 src&#x2F;index.ts</h4><pre class="line-numbers language-javascript" data-language="javascript"><code class="language-javascript"><span class="token comment">/**
 * 在示例或者官网提到的所有 API 都在这里统一导出
 */</span>
<span class="token comment">// 最关键的三个，手动加载微应用、基于路由配置、启动 qiankun</span>
<span class="token keyword">export</span> <span class="token punctuation">&#123;</span> loadMicroApp<span class="token punctuation">,</span> registerMicroApps<span class="token punctuation">,</span> start <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">"./apis"</span><span class="token punctuation">;</span>
<span class="token comment">// 全局状态</span>
<span class="token keyword">export</span> <span class="token punctuation">&#123;</span> initGlobalState <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">"./globalState"</span><span class="token punctuation">;</span>
<span class="token comment">// 全局的未捕获异常处理器</span>
<span class="token keyword">export</span> <span class="token operator">*</span> <span class="token keyword">from</span> <span class="token string">"./errorHandler"</span><span class="token punctuation">;</span>
<span class="token comment">// setDefaultMountApp 设置主应用启动后默认进入哪个微应用、runAfterFirstMounted 设置当第一个微应用挂载以后需要调用的一些方法</span>
<span class="token keyword">export</span> <span class="token operator">*</span> <span class="token keyword">from</span> <span class="token string">"./effects"</span><span class="token punctuation">;</span>
<span class="token comment">// 类型定义</span>
<span class="token keyword">export</span> <span class="token operator">*</span> <span class="token keyword">from</span> <span class="token string">"./interfaces"</span><span class="token punctuation">;</span>
<span class="token comment">// 预加载</span>
<span class="token keyword">export</span> <span class="token punctuation">&#123;</span> prefetchImmediately <span class="token keyword">as</span> prefetchApps <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">"./prefetch"</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h4 id="registerMicroApps"><a href="#registerMicroApps" class="headerlink" title="registerMicroApps"></a>registerMicroApps</h4><pre class="line-numbers language-javascript" data-language="javascript"><code class="language-javascript"><span class="token comment">/**
 * 注册微应用，基于路由配置
 * @param apps = [
 *  &#123;
 *    name: 'react16', 微应用名称
 *    entry: '//localhost:7100', 地址
 *    container: '#subapp-viewport', 挂载点
 *    loader,
 *    activeRule: '/react16' 相应的路由匹配相应的应用
 *  &#125;,
 *  ...
 * ]
 * @param lifeCycles = &#123; ...全局各个生命周期方法对象 &#125;
 */</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> registerMicroApps<span class="token operator">&lt;</span><span class="token constant">T</span> <span class="token keyword">extends</span> <span class="token class-name">object</span> <span class="token operator">=</span> <span class="token punctuation">&#123;</span><span class="token punctuation">&#125;</span><span class="token operator">></span><span class="token punctuation">(</span>
  <span class="token literal-property property">apps</span><span class="token operator">:</span> Array<span class="token operator">&lt;</span>RegistrableApp<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">>></span><span class="token punctuation">,</span>
  lifeCycles<span class="token operator">?</span><span class="token operator">:</span> FrameworkLifeCycles<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span><span class="token punctuation">,</span>
<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
  <span class="token comment">// 防止微应用重复注册，得到所有没有被注册的微应用列表</span>
  <span class="token keyword">const</span> unregisteredApps <span class="token operator">=</span> apps<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token parameter">app</span> <span class="token operator">=></span> <span class="token operator">!</span>microApps<span class="token punctuation">.</span><span class="token function">some</span><span class="token punctuation">(</span><span class="token parameter">registeredApp</span> <span class="token operator">=></span> registeredApp<span class="token punctuation">.</span>name <span class="token operator">===</span> app<span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// 所有的微应用 = 已注册 + 未注册的(将要被注册的)</span>
  microApps <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token operator">...</span>microApps<span class="token punctuation">,</span> <span class="token operator">...</span>unregisteredApps<span class="token punctuation">]</span><span class="token punctuation">;</span>

  <span class="token comment">// 注册每一个微应用</span>
  unregisteredApps<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">app</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// 注册时提供的微应用基本信息</span>
    <span class="token keyword">const</span> <span class="token punctuation">&#123;</span> name<span class="token punctuation">,</span> activeRule<span class="token punctuation">,</span> loader <span class="token operator">=</span> noop<span class="token punctuation">,</span> props<span class="token punctuation">,</span> <span class="token operator">...</span>appConfig <span class="token punctuation">&#125;</span> <span class="token operator">=</span> app<span class="token punctuation">;</span>

    <span class="token comment">// 调用 single-spa 的 registerApplication 方法注册微应用</span>
    <span class="token function">registerApplication</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span>
      <span class="token comment">// 微应用名称</span>
      name<span class="token punctuation">,</span>
      <span class="token comment">// 微应用的加载方法，Promise&lt;生命周期方法组成的对象></span>
      <span class="token function-variable function">app</span><span class="token operator">:</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
        <span class="token comment">// 加载微应用时主应用显示 loading 状态</span>
        <span class="token function">loader</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 这句可以忽略，目的是在 single-spa 执行这个加载方法时让出线程，让其它微应用的加载方法都开始执行</span>
        <span class="token keyword">await</span> frameworkStartedDefer<span class="token punctuation">.</span>promise<span class="token punctuation">;</span>

        <span class="token comment">// 核心、精髓、难点所在，负责加载微应用，然后一大堆处理，返回 bootstrap、mount、unmount、update 这个几个生命周期</span>
        <span class="token keyword">const</span> <span class="token punctuation">&#123;</span> mount<span class="token punctuation">,</span> <span class="token operator">...</span>otherMicroAppConfigs <span class="token punctuation">&#125;</span> <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">loadApp</span><span class="token punctuation">(</span>
          <span class="token comment">// 微应用的配置信息</span>
          <span class="token punctuation">&#123;</span> name<span class="token punctuation">,</span> props<span class="token punctuation">,</span> <span class="token operator">...</span>appConfig <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
          <span class="token comment">// start 方法执行时设置的配置对象</span>
          frameworkConfiguration<span class="token punctuation">,</span>
          <span class="token comment">// 注册微应用时提供的全局生命周期对象</span>
          lifeCycles<span class="token punctuation">,</span>
        <span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token punctuation">&#123;</span>
          <span class="token literal-property property">mount</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">loader</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">...</span><span class="token function">toArray</span><span class="token punctuation">(</span>mount<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">loader</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
          <span class="token operator">...</span>otherMicroAppConfigs<span class="token punctuation">,</span>
        <span class="token punctuation">&#125;</span><span class="token punctuation">;</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
      <span class="token comment">// 微应用的激活条件</span>
      <span class="token literal-property property">activeWhen</span><span class="token operator">:</span> activeRule<span class="token punctuation">,</span>
      <span class="token comment">// 传递给微应用的 props</span>
      <span class="token literal-property property">customProps</span><span class="token operator">:</span> props<span class="token punctuation">,</span>
    <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h4 id="start"><a href="#start" class="headerlink" title="start"></a>start</h4><pre class="line-numbers language-javascript" data-language="javascript"><code class="language-javascript"><span class="token comment">/**
 * 启动 qiankun
 * @param opts start 方法的配置对象
 */</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">start</span><span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">opts</span><span class="token operator">:</span> FrameworkConfiguration <span class="token operator">=</span> <span class="token punctuation">&#123;</span><span class="token punctuation">&#125;</span></span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
  <span class="token comment">// qiankun 框架默认开启预加载、单例模式、样式沙箱</span>
  frameworkConfiguration <span class="token operator">=</span> <span class="token punctuation">&#123;</span>
    <span class="token literal-property property">prefetch</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    <span class="token literal-property property">singular</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    <span class="token literal-property property">sandbox</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    <span class="token operator">...</span>opts<span class="token punctuation">,</span>
  <span class="token punctuation">&#125;</span><span class="token punctuation">;</span>
  <span class="token comment">// 从这里可以看出 start 方法支持的参数不止官网文档说的那些，比如 urlRerouteOnly，这个是 single-spa 的 start 方法支持的</span>
  <span class="token keyword">const</span> <span class="token punctuation">&#123;</span> prefetch<span class="token punctuation">,</span> sandbox<span class="token punctuation">,</span> singular<span class="token punctuation">,</span> urlRerouteOnly<span class="token punctuation">,</span> <span class="token operator">...</span>importEntryOpts <span class="token punctuation">&#125;</span> <span class="token operator">=</span>
    frameworkConfiguration<span class="token punctuation">;</span>

  <span class="token comment">// 预加载</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>prefetch<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// 执行预加载策略，参数分别为微应用列表、预加载策略、&#123; fetch、getPublicPath、getTemplate &#125;</span>
    <span class="token function">doPrefetchStrategy</span><span class="token punctuation">(</span>microApps<span class="token punctuation">,</span> prefetch<span class="token punctuation">,</span> importEntryOpts<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">&#125;</span>

  <span class="token comment">// 样式沙箱</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>sandbox<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>window<span class="token punctuation">.</span>Proxy<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
      console<span class="token punctuation">.</span><span class="token function">warn</span><span class="token punctuation">(</span>
        <span class="token string">"[qiankun] Miss window.Proxy, proxySandbox will degenerate into snapshotSandbox"</span>
      <span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token comment">// 快照沙箱不支持非 singular 模式</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>singular<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>
          <span class="token string">"[qiankun] singular is forced to be true when sandbox enable but proxySandbox unavailable"</span>
        <span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 如果开启沙箱，会强制使用单例模式</span>
        frameworkConfiguration<span class="token punctuation">.</span>singular <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
      <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>
  <span class="token punctuation">&#125;</span>

  <span class="token comment">// 执行 single-spa 的 start 方法，启动 single-spa</span>
  <span class="token function">startSingleSpa</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span> urlRerouteOnly <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  frameworkStartedDefer<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h4 id="预加载-doPrefetchStrategy"><a href="#预加载-doPrefetchStrategy" class="headerlink" title="预加载 - doPrefetchStrategy"></a>预加载 - doPrefetchStrategy</h4><pre class="line-numbers language-javascript" data-language="javascript"><code class="language-javascript"><span class="token comment">/**
 * 执行预加载策略，qiankun 支持四种
 * @param apps 所有的微应用
 * @param prefetchStrategy 预加载策略，四种 =》
 *  1、true，第一个微应用挂载以后加载其它微应用的静态资源，利用的是 single-spa 提供的 single-spa:first-mount 事件来实现的
 *  2、string[]，微应用名称数组，在第一个微应用挂载以后加载指定的微应用的静态资源
 *  3、all，主应用执行 start 以后就直接开始预加载所有微应用的静态资源
 *  4、自定义函数，返回两个微应用组成的数组，一个是关键微应用组成的数组，需要马上就执行预加载的微应用，一个是普通的微应用组成的数组，在第一个微应用挂载以后预加载这些微应用的静态资源
 * @param importEntryOpts = &#123; fetch, getPublicPath, getTemplate &#125;
 */</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">doPrefetchStrategy</span><span class="token punctuation">(</span>
  <span class="token parameter"><span class="token literal-property property">apps</span><span class="token operator">:</span> AppMetadata<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token literal-property property">prefetchStrategy</span><span class="token operator">:</span> PrefetchStrategy<span class="token punctuation">,</span>
  importEntryOpts<span class="token operator">?</span><span class="token operator">:</span> ImportEntryOpts<span class="token punctuation">,</span></span>
<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
  <span class="token comment">// 定义函数，函数接收一个微应用名称组成的数组，然后从微应用列表中返回这些名称所对应的微应用，最后得到一个数组[&#123;name, entry&#125;, ...]</span>
  <span class="token keyword">const</span> appsName2Apps <span class="token operator">=</span> <span class="token punctuation">(</span>names<span class="token operator">:</span> string<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">:</span> AppMetadata<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=></span> apps<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token parameter">app</span> <span class="token operator">=></span> names<span class="token punctuation">.</span><span class="token function">includes</span><span class="token punctuation">(</span>app<span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">if</span> <span class="token punctuation">(</span>Array<span class="token punctuation">.</span><span class="token function">isArray</span><span class="token punctuation">(</span>prefetchStrategy<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// 说明加载策略是一个数组，当第一个微应用挂载之后开始加载数组内由用户指定的微应用资源，数组内的每一项表示一个微应用的名称</span>
    <span class="token function">prefetchAfterFirstMounted</span><span class="token punctuation">(</span><span class="token function">appsName2Apps</span><span class="token punctuation">(</span>prefetchStrategy <span class="token keyword">as</span> string<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span> importEntryOpts<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isFunction</span><span class="token punctuation">(</span>prefetchStrategy<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// 加载策略是一个自定义的函数，可完全自定义应用资源的加载时机（首屏应用、次屏应用)</span>
    <span class="token punctuation">(</span><span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
      <span class="token comment">// critical rendering apps would be prefetch as earlier as possible，关键的应用程序应该尽可能早的预取</span>
      <span class="token comment">// 执行加载策略函数，函数会返回两个数组，一个关键的应用程序数组，会立即执行预加载动作，另一个是在第一个微应用挂载以后执行微应用静态资源的预加载</span>
      <span class="token keyword">const</span> <span class="token punctuation">&#123;</span> criticalAppNames <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> minorAppsName <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">&#125;</span> <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">prefetchStrategy</span><span class="token punctuation">(</span>apps<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token comment">// 立即预加载这些关键微应用程序的静态资源</span>
      <span class="token function">prefetchImmediately</span><span class="token punctuation">(</span><span class="token function">appsName2Apps</span><span class="token punctuation">(</span>criticalAppNames<span class="token punctuation">)</span><span class="token punctuation">,</span> importEntryOpts<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token comment">// 当第一个微应用挂载以后预加载这些微应用的静态资源</span>
      <span class="token function">prefetchAfterFirstMounted</span><span class="token punctuation">(</span><span class="token function">appsName2Apps</span><span class="token punctuation">(</span>minorAppsName<span class="token punctuation">)</span><span class="token punctuation">,</span> importEntryOpts<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// 加载策略是默认的 true 或者 all</span>
    <span class="token keyword">switch</span> <span class="token punctuation">(</span>prefetchStrategy<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
      <span class="token keyword">case</span> <span class="token boolean">true</span><span class="token operator">:</span>
        <span class="token comment">// 第一个微应用挂载之后开始加载其它微应用的静态资源</span>
        <span class="token function">prefetchAfterFirstMounted</span><span class="token punctuation">(</span>apps<span class="token punctuation">,</span> importEntryOpts<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">break</span><span class="token punctuation">;</span>

      <span class="token keyword">case</span> <span class="token string">'all'</span><span class="token operator">:</span>
        <span class="token comment">// 在主应用执行 start 以后就开始加载所有微应用的静态资源</span>
        <span class="token function">prefetchImmediately</span><span class="token punctuation">(</span>apps<span class="token punctuation">,</span> importEntryOpts<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">break</span><span class="token punctuation">;</span>

      <span class="token keyword">default</span><span class="token operator">:</span>
        <span class="token keyword">break</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
  <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span>

<span class="token comment">// 判断是否为弱网环境</span>
<span class="token keyword">const</span> isSlowNetwork <span class="token operator">=</span> navigator<span class="token punctuation">.</span>connection
  <span class="token operator">?</span> navigator<span class="token punctuation">.</span>connection<span class="token punctuation">.</span>saveData <span class="token operator">||</span>
    <span class="token punctuation">(</span>navigator<span class="token punctuation">.</span>connection<span class="token punctuation">.</span>type <span class="token operator">!==</span> <span class="token string">'wifi'</span> <span class="token operator">&amp;&amp;</span>
      navigator<span class="token punctuation">.</span>connection<span class="token punctuation">.</span>type <span class="token operator">!==</span> <span class="token string">'ethernet'</span> <span class="token operator">&amp;&amp;</span>
      <span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">(2|3)g</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">.</span><span class="token function">test</span><span class="token punctuation">(</span>navigator<span class="token punctuation">.</span>connection<span class="token punctuation">.</span>effectiveType<span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">;</span>

<span class="token comment">/**
 * prefetch assets, do nothing while in mobile network
 * 预加载静态资源，在移动网络下什么都不做
 * @param entry
 * @param opts
 */</span>
<span class="token keyword">function</span> <span class="token function">prefetch</span><span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">entry</span><span class="token operator">:</span> Entry<span class="token punctuation">,</span> opts<span class="token operator">?</span><span class="token operator">:</span> ImportEntryOpts</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">&#123;</span>
  <span class="token comment">// 弱网环境下不执行预加载</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>navigator<span class="token punctuation">.</span>onLine <span class="token operator">||</span> isSlowNetwork<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// Don't prefetch if in a slow network or offline</span>
    <span class="token keyword">return</span><span class="token punctuation">;</span>
  <span class="token punctuation">&#125;</span>

  <span class="token comment">// 通过时间切片的方式去加载静态资源，在浏览器空闲时去执行回调函数，避免浏览器卡顿</span>
  <span class="token function">requestIdleCallback</span><span class="token punctuation">(</span><span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// 得到加载静态资源的函数</span>
    <span class="token keyword">const</span> <span class="token punctuation">&#123;</span> getExternalScripts<span class="token punctuation">,</span> getExternalStyleSheets <span class="token punctuation">&#125;</span> <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">importEntry</span><span class="token punctuation">(</span>entry<span class="token punctuation">,</span> opts<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 样式</span>
    <span class="token function">requestIdleCallback</span><span class="token punctuation">(</span>getExternalStyleSheets<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// js 脚本</span>
    <span class="token function">requestIdleCallback</span><span class="token punctuation">(</span>getExternalScripts<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span>

<span class="token comment">/**
 * 在第一个微应用挂载之后开始加载 apps 中指定的微应用的静态资源
 * 通过监听 single-spa 提供的 single-spa:first-mount 事件来实现，该事件在第一个微应用挂载以后会被触发
 * @param apps 需要被预加载静态资源的微应用列表，[&#123; name, entry &#125;, ...]
 * @param opts = &#123; fetch , getPublicPath, getTemplate &#125;
 */</span>
<span class="token keyword">function</span> <span class="token function">prefetchAfterFirstMounted</span><span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">apps</span><span class="token operator">:</span> AppMetadata<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> opts<span class="token operator">?</span><span class="token operator">:</span> ImportEntryOpts</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">&#123;</span>
  <span class="token comment">// 监听 single-spa:first-mount 事件</span>
  window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'single-spa:first-mount'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token function">listener</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// 已挂载的微应用</span>
    <span class="token keyword">const</span> mountedApps <span class="token operator">=</span> <span class="token function">getMountedApps</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 从预加载的微应用列表中过滤出未挂载的微应用</span>
    <span class="token keyword">const</span> notMountedApps <span class="token operator">=</span> apps<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token parameter">app</span> <span class="token operator">=></span> mountedApps<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span>app<span class="token punctuation">.</span>name<span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// 开发环境打印日志，已挂载的微应用和未挂载的微应用分别有哪些</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">NODE_ENV</span> <span class="token operator">===</span> <span class="token string">'development'</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[qiankun] prefetch starting after </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>mountedApps<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string"> mounted...</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> notMountedApps<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token comment">// 循环加载微应用的静态资源</span>
    notMountedApps<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">&#123;</span> entry <span class="token punctuation">&#125;</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">prefetch</span><span class="token punctuation">(</span>entry<span class="token punctuation">,</span> opts<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// 移除 single-spa:first-mount 事件</span>
    window<span class="token punctuation">.</span><span class="token function">removeEventListener</span><span class="token punctuation">(</span><span class="token string">'single-spa:first-mount'</span><span class="token punctuation">,</span> listener<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span>

<span class="token comment">/**
 * 在执行 start 启动 qiankun 之后立即预加载所有微应用的静态资源
 * @param apps 需要被预加载静态资源的微应用列表，[&#123; name, entry &#125;, ...]
 * @param opts = &#123; fetch , getPublicPath, getTemplate &#125;
 */</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">prefetchImmediately</span><span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">apps</span><span class="token operator">:</span> AppMetadata<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> opts<span class="token operator">?</span><span class="token operator">:</span> ImportEntryOpts</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">&#123;</span>
  <span class="token comment">// 开发环境打印日志</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">NODE_ENV</span> <span class="token operator">===</span> <span class="token string">'development'</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'[qiankun] prefetch starting for apps...'</span><span class="token punctuation">,</span> apps<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">&#125;</span>

  <span class="token comment">// 加载所有微应用的静态资源</span>
  apps<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">&#123;</span> entry <span class="token punctuation">&#125;</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">prefetch</span><span class="token punctuation">(</span>entry<span class="token punctuation">,</span> opts<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h4 id="应用间通信-initGlobalState"><a href="#应用间通信-initGlobalState" class="headerlink" title="应用间通信 initGlobalState"></a>应用间通信 initGlobalState</h4><pre class="line-numbers language-javascript" data-language="javascript"><code class="language-javascript"><span class="token comment">// 触发全局监听，执行所有应用注册的回调函数</span>
<span class="token keyword">function</span> <span class="token function">emitGlobal</span><span class="token punctuation">(</span>
  <span class="token parameter"><span class="token literal-property property">state</span><span class="token operator">:</span> Record<span class="token operator">&lt;</span>string<span class="token punctuation">,</span> any<span class="token operator">></span><span class="token punctuation">,</span>
  <span class="token literal-property property">prevState</span><span class="token operator">:</span> Record<span class="token operator">&lt;</span>string<span class="token punctuation">,</span> any<span class="token operator">></span></span>
<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
  <span class="token comment">// 循环遍历，执行所有应用注册的回调函数</span>
  Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>deps<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">id</span><span class="token operator">:</span> string</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>deps<span class="token punctuation">[</span>id<span class="token punctuation">]</span> <span class="token keyword">instanceof</span> <span class="token class-name">Function</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
      deps<span class="token punctuation">[</span>id<span class="token punctuation">]</span><span class="token punctuation">(</span><span class="token function">cloneDeep</span><span class="token punctuation">(</span>state<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">cloneDeep</span><span class="token punctuation">(</span>prevState<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span>

<span class="token comment">/**
 * 定义全局状态，并返回通信方法，一般由主应用调用，微应用通过 props 获取通信方法。
 * @param state 全局状态，&#123; key: value &#125;
 */</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">initGlobalState</span><span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">state</span><span class="token operator">:</span> Record<span class="token operator">&lt;</span>string<span class="token punctuation">,</span> any<span class="token operator">></span> <span class="token operator">=</span> <span class="token punctuation">&#123;</span><span class="token punctuation">&#125;</span></span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>state <span class="token operator">===</span> globalState<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    console<span class="token punctuation">.</span><span class="token function">warn</span><span class="token punctuation">(</span><span class="token string">"[qiankun] state has not changed！"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// 方法有可能被重复调用，将已有的全局状态克隆一份，为空则是第一次调用 initGlobalState 方法，不为空则非第一次次调用</span>
    <span class="token keyword">const</span> prevGlobalState <span class="token operator">=</span> <span class="token function">cloneDeep</span><span class="token punctuation">(</span>globalState<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 将传递的状态克隆一份赋值为 globalState</span>
    globalState <span class="token operator">=</span> <span class="token function">cloneDeep</span><span class="token punctuation">(</span>state<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 触发全局监听，当然在这个位置调用，正常情况下没啥反应，因为现在还没有应用注册回调函数</span>
    <span class="token function">emitGlobal</span><span class="token punctuation">(</span>globalState<span class="token punctuation">,</span> prevGlobalState<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">&#125;</span>
  <span class="token comment">// 返回通信方法，参数表示应用 id，true 表示自己是主应用调用</span>
  <span class="token keyword">return</span> <span class="token function">getMicroAppStateActions</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">global-</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span><span class="token operator">+</span><span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span>

<span class="token comment">/**
 * 返回通信方法
 * @param id 应用 id
 * @param isMaster 表明调用的应用是否为主应用，在主应用初始化全局状态时，initGlobalState 内部调用该方法时会传递 true，其它都为 false
 */</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">getMicroAppStateActions</span><span class="token punctuation">(</span>
  <span class="token parameter"><span class="token literal-property property">id</span><span class="token operator">:</span> string<span class="token punctuation">,</span>
  isMaster<span class="token operator">?</span><span class="token operator">:</span> boolean</span>
<span class="token punctuation">)</span><span class="token operator">:</span> MicroAppStateActions <span class="token punctuation">&#123;</span>
  <span class="token keyword">return</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">/**
     * 全局依赖监听，为指定应用（id = 应用id）注册回调函数
     * 依赖数据结构为：
     * &#123;
     *   &#123;id&#125;: callback
     * &#125;
     *
     * @param callback 注册的回调函数
     * @param fireImmediately 是否立即执行回调
     */</span>
    <span class="token function">onGlobalStateChange</span><span class="token punctuation">(</span>
      <span class="token parameter"><span class="token literal-property property">callback</span><span class="token operator">:</span> OnGlobalStateChangeCallback<span class="token punctuation">,</span>
      fireImmediately<span class="token operator">?</span><span class="token operator">:</span> boolean</span>
    <span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
      <span class="token comment">// 回调函数必须为 function</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token punctuation">(</span>callback <span class="token keyword">instanceof</span> <span class="token class-name">Function</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">"[qiankun] callback must be function!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span><span class="token punctuation">;</span>
      <span class="token punctuation">&#125;</span>
      <span class="token comment">// 如果回调函数已经存在，重复注册时给出覆盖提示信息</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>deps<span class="token punctuation">[</span>id<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        console<span class="token punctuation">.</span><span class="token function">warn</span><span class="token punctuation">(</span>
          <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[qiankun] '</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>id<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">' global listener already exists before this, new listener will overwrite it.</span><span class="token template-punctuation string">`</span></span>
        <span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">&#125;</span>
      <span class="token comment">// id 为一个应用 id，一个应用对应一个回调</span>
      deps<span class="token punctuation">[</span>id<span class="token punctuation">]</span> <span class="token operator">=</span> callback<span class="token punctuation">;</span>
      <span class="token comment">// 克隆全局状态</span>
      <span class="token keyword">const</span> cloneState <span class="token operator">=</span> <span class="token function">cloneDeep</span><span class="token punctuation">(</span>globalState<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token comment">// 如果需要，立即出发回调执行</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>fireImmediately<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token function">callback</span><span class="token punctuation">(</span>cloneState<span class="token punctuation">,</span> cloneState<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>

    <span class="token comment">/**
     * setGlobalState 更新 store 数据
     *
     * 1. 对新输入 state 的第一层属性做校验，如果是主应用则可以添加新的一级属性进来，也可以更新已存在的一级属性，
     *    如果是微应用，则只能更新已存在的一级属性，不可以新增一级属性
     * 2. 触发全局监听，执行所有应用注册的回调函数，以达到应用间通信的目的
     *
     * @param state 新的全局状态
     */</span>
    <span class="token function">setGlobalState</span><span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">state</span><span class="token operator">:</span> Record<span class="token operator">&lt;</span>string<span class="token punctuation">,</span> any<span class="token operator">></span> <span class="token operator">=</span> <span class="token punctuation">&#123;</span><span class="token punctuation">&#125;</span></span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>state <span class="token operator">===</span> globalState<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        console<span class="token punctuation">.</span><span class="token function">warn</span><span class="token punctuation">(</span><span class="token string">"[qiankun] state has not changed！"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
      <span class="token punctuation">&#125;</span>

      <span class="token comment">// 记录旧的全局状态中被改变的 key</span>
      <span class="token keyword">const</span> <span class="token literal-property property">changeKeys</span><span class="token operator">:</span> string<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
      <span class="token comment">// 旧的全局状态</span>
      <span class="token keyword">const</span> prevGlobalState <span class="token operator">=</span> <span class="token function">cloneDeep</span><span class="token punctuation">(</span>globalState<span class="token punctuation">)</span><span class="token punctuation">;</span>
      globalState <span class="token operator">=</span> <span class="token function">cloneDeep</span><span class="token punctuation">(</span>
        <span class="token comment">// 循环遍历新状态中的所有 key</span>
        Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>state<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">_globalState<span class="token punctuation">,</span> changeKey</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
          <span class="token keyword">if</span> <span class="token punctuation">(</span>isMaster <span class="token operator">||</span> _globalState<span class="token punctuation">.</span><span class="token function">hasOwnProperty</span><span class="token punctuation">(</span>changeKey<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token comment">// 主应用 或者 旧的全局状态存在该 key 时才进来，说明只有主应用才可以新增属性，微应用只可以更新已存在的属性值，且不论主应用微应用只能更新一级属性</span>
            <span class="token comment">// 记录被改变的key</span>
            changeKeys<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>changeKey<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token comment">// 更新旧状态中对应的 key value</span>
            <span class="token keyword">return</span> Object<span class="token punctuation">.</span><span class="token function">assign</span><span class="token punctuation">(</span>_globalState<span class="token punctuation">,</span> <span class="token punctuation">&#123;</span>
              <span class="token punctuation">[</span>changeKey<span class="token punctuation">]</span><span class="token operator">:</span> state<span class="token punctuation">[</span>changeKey<span class="token punctuation">]</span><span class="token punctuation">,</span>
            <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
          <span class="token punctuation">&#125;</span>
          console<span class="token punctuation">.</span><span class="token function">warn</span><span class="token punctuation">(</span>
            <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[qiankun] '</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>changeKey<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">' not declared when init state！</span><span class="token template-punctuation string">`</span></span>
          <span class="token punctuation">)</span><span class="token punctuation">;</span>
          <span class="token keyword">return</span> _globalState<span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span><span class="token punctuation">,</span> globalState<span class="token punctuation">)</span>
      <span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>changeKeys<span class="token punctuation">.</span>length <span class="token operator">===</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        console<span class="token punctuation">.</span><span class="token function">warn</span><span class="token punctuation">(</span><span class="token string">"[qiankun] state has not changed！"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
      <span class="token punctuation">&#125;</span>
      <span class="token comment">// 触发全局监听</span>
      <span class="token function">emitGlobal</span><span class="token punctuation">(</span>globalState<span class="token punctuation">,</span> prevGlobalState<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>

    <span class="token comment">// 注销该应用下的依赖</span>
    <span class="token function">offGlobalStateChange</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
      <span class="token keyword">delete</span> deps<span class="token punctuation">[</span>id<span class="token punctuation">]</span><span class="token punctuation">;</span>
      <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
  <span class="token punctuation">&#125;</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h4 id="全局未捕获异常处理器"><a href="#全局未捕获异常处理器" class="headerlink" title="全局未捕获异常处理器"></a>全局未捕获异常处理器</h4><pre class="line-numbers language-javascript" data-language="javascript"><code class="language-javascript"><span class="token comment">/**
 * 整个文件的逻辑一眼明了，整个框架提供了两种全局异常捕获，一个是 single-spa 提供的，另一个是 qiankun 自己的，你只需提供相应的回调函数即可
 */</span>

<span class="token comment">// single-spa 的异常捕获</span>
<span class="token keyword">export</span> <span class="token punctuation">&#123;</span> addErrorHandler<span class="token punctuation">,</span> removeErrorHandler <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">"single-spa"</span><span class="token punctuation">;</span>

<span class="token comment">// qiankun 的异常捕获</span>
<span class="token comment">// 监听了 error 和 unhandlerejection 事件</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">addGlobalUncaughtErrorHandler</span><span class="token punctuation">(</span>
  <span class="token parameter"><span class="token literal-property property">errorHandler</span><span class="token operator">:</span> OnErrorEventHandlerNonNull</span>
<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">&#123;</span>
  window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"error"</span><span class="token punctuation">,</span> errorHandler<span class="token punctuation">)</span><span class="token punctuation">;</span>
  window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"unhandledrejection"</span><span class="token punctuation">,</span> errorHandler<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span>

<span class="token comment">// 移除 error 和 unhandlerejection 事件监听</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">removeGlobalUncaughtErrorHandler</span><span class="token punctuation">(</span>
  <span class="token function-variable function">errorHandler</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span>args<span class="token operator">:</span> any<span class="token punctuation">[</span><span class="token punctuation">]</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> any
<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
  window<span class="token punctuation">.</span><span class="token function">removeEventListener</span><span class="token punctuation">(</span><span class="token string">"error"</span><span class="token punctuation">,</span> errorHandler<span class="token punctuation">)</span><span class="token punctuation">;</span>
  window<span class="token punctuation">.</span><span class="token function">removeEventListener</span><span class="token punctuation">(</span><span class="token string">"unhandledrejection"</span><span class="token punctuation">,</span> errorHandler<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h4 id="setDefaultMountApp"><a href="#setDefaultMountApp" class="headerlink" title="setDefaultMountApp"></a>setDefaultMountApp</h4><pre class="line-numbers language-javascript" data-language="javascript"><code class="language-javascript"><span class="token comment">/**
 * 设置主应用启动后默认进入的微应用，其实是规定了第一个微应用挂载完成后决定默认进入哪个微应用
 * 利用的是 single-spa 的 single-spa:no-app-change 事件，该事件在所有微应用状态改变结束后（即发生路由切换且新的微应用已经被挂载完成）触发
 * @param defaultAppLink 微应用的链接，比如 /react16
 */</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">setDefaultMountApp</span><span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">defaultAppLink</span><span class="token operator">:</span> string</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
  <span class="token comment">// 当事件触发时就说明微应用已经挂载完成，但这里只监听了一次，因为事件被触发以后就移除了监听，所以说是主应用启动后默认进入的微应用，且只执行了一次的原因</span>
  window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"single-spa:no-app-change"</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token function">listener</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// 说明微应用已经挂载完成，获取挂载的微应用列表，再次确认确实有微应用挂载了，其实这个确认没啥必要</span>
    <span class="token keyword">const</span> mountedApps <span class="token operator">=</span> <span class="token function">getMountedApps</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>mountedApps<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
      <span class="token comment">// 这个是 single-spa 提供的一个 api，通过触发 window.location.hash 或者 pushState 更改路由，切换微应用</span>
      <span class="token function">navigateToUrl</span><span class="token punctuation">(</span>defaultAppLink<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token comment">// 触发一次以后，就移除该事件的监听函数，后续的路由切换（事件触发）时就不再响应</span>
    window<span class="token punctuation">.</span><span class="token function">removeEventListener</span><span class="token punctuation">(</span><span class="token string">"single-spa:no-app-change"</span><span class="token punctuation">,</span> listener<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span>

<span class="token comment">// 这个 api 和 setDefaultMountApp 作用一致，官网也提到，兼容老版本的一个 api</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">runDefaultMountEffects</span><span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">defaultAppLink</span><span class="token operator">:</span> string</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
  console<span class="token punctuation">.</span><span class="token function">warn</span><span class="token punctuation">(</span>
    <span class="token string">"[qiankun] runDefaultMountEffects will be removed in next version, please use setDefaultMountApp instead"</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token function">setDefaultMountApp</span><span class="token punctuation">(</span>defaultAppLink<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h4 id="runAfterFirstMounted"><a href="#runAfterFirstMounted" class="headerlink" title="runAfterFirstMounted"></a>runAfterFirstMounted</h4><pre class="line-numbers language-javascript" data-language="javascript"><code class="language-javascript"><span class="token comment">/**
 * 第一个微应用 mount 后需要调用的方法，比如开启一些监控或者埋点脚本
 * 同样利用的 single-spa 的 single-spa:first-mount 事件，当第一个微应用挂载以后会触发
 * @param effect 回调函数，当第一个微应用挂载以后要做的事情
 */</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">runAfterFirstMounted</span><span class="token punctuation">(</span><span class="token function-variable function">effect</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">void</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
  <span class="token comment">// can not use addEventListener once option for ie support</span>
  window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"single-spa:first-mount"</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token function">listener</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">NODE_ENV</span> <span class="token operator">===</span> <span class="token string">"development"</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
      console<span class="token punctuation">.</span><span class="token function">timeEnd</span><span class="token punctuation">(</span>firstMountLogLabel<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token function">effect</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// 这里不移除也没事，因为这个事件后续不会再被触发了</span>
    window<span class="token punctuation">.</span><span class="token function">removeEventListener</span><span class="token punctuation">(</span><span class="token string">"single-spa:first-mount"</span><span class="token punctuation">,</span> listener<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h4 id="手动加载微应用-loadMicroApp"><a href="#手动加载微应用-loadMicroApp" class="headerlink" title="手动加载微应用 loadMicroApp"></a>手动加载微应用 loadMicroApp</h4><pre class="line-numbers language-javascript" data-language="javascript"><code class="language-javascript"><span class="token comment">/**
 * 手动加载一个微应用，是通过 single-spa 的 mountRootParcel api 实现的，返回微应用实例
 * @param app = &#123; name, entry, container, props &#125;
 * @param configuration 配置对象
 * @param lifeCycles 还支持一个全局生命周期配置对象，这个参数官方文档没提到
 */</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> loadMicroApp<span class="token operator">&lt;</span><span class="token constant">T</span> <span class="token keyword">extends</span> <span class="token class-name">object</span> <span class="token operator">=</span> <span class="token punctuation">&#123;</span><span class="token punctuation">&#125;</span><span class="token operator">></span><span class="token punctuation">(</span>
  <span class="token literal-property property">app</span><span class="token operator">:</span> LoadableApp<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span><span class="token punctuation">,</span>
  configuration<span class="token operator">?</span><span class="token operator">:</span> FrameworkConfiguration<span class="token punctuation">,</span>
  lifeCycles<span class="token operator">?</span><span class="token operator">:</span> FrameworkLifeCycles<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span><span class="token punctuation">,</span>
<span class="token punctuation">)</span><span class="token operator">:</span> MicroApp <span class="token punctuation">&#123;</span>
  <span class="token keyword">const</span> <span class="token punctuation">&#123;</span> props <span class="token punctuation">&#125;</span> <span class="token operator">=</span> app<span class="token punctuation">;</span>
  <span class="token comment">// single-spa 的 mountRootParcel api</span>
  <span class="token keyword">return</span> <span class="token function">mountRootParcel</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">loadApp</span><span class="token punctuation">(</span>app<span class="token punctuation">,</span> configuration <span class="token operator">??</span> frameworkConfiguration<span class="token punctuation">,</span> lifeCycles<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">&#123;</span>
    <span class="token literal-property property">domElement</span><span class="token operator">:</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">'div'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token operator">...</span>props<span class="token punctuation">,</span>
  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h4 id="qiankun-的核心-loadApp"><a href="#qiankun-的核心-loadApp" class="headerlink" title="qiankun 的核心 loadApp"></a>qiankun 的核心 loadApp</h4><p>接下来介绍 loadApp 方法，个人认为 qiankun 的核心代码可以说大部分都在这里，当然这也是整个框架的精髓和难点所在</p>
<pre class="line-numbers language-javascript" data-language="javascript"><code class="language-javascript"><span class="token comment">/**
 * 完成了以下几件事：
 *  1、通过 HTML Entry 的方式远程加载微应用，得到微应用的 html 模版（首屏内容）、JS 脚本执行器、静态经资源路径
 *  2、样式隔离，shadow DOM 或者 scoped css 两种方式
 *  3、渲染微应用
 *  4、运行时沙箱，JS 沙箱、样式沙箱
 *  5、合并沙箱传递出来的 生命周期方法、用户传递的生命周期方法、框架内置的生命周期方法，将这些生命周期方法统一整理，导出一个生命周期对象，
 * 供 single-spa 的 registerApplication 方法使用，这个对象就相当于使用 single-spa 时你的微应用导出的那些生命周期方法，只不过 qiankun
 * 额外填了一些生命周期方法，做了一些事情
 *  6、给微应用注册通信方法并返回通信方法，然后会将通信方法通过 props 注入到微应用
 * @param app 微应用配置对象
 * @param configuration start 方法执行时设置的配置对象
 * @param lifeCycles 注册微应用时提供的全局生命周期对象
 */</span>
<span class="token keyword">export</span> <span class="token keyword">async</span> <span class="token keyword">function</span> loadApp<span class="token operator">&lt;</span><span class="token constant">T</span> <span class="token keyword">extends</span> <span class="token class-name">object</span><span class="token operator">></span><span class="token punctuation">(</span>
  <span class="token literal-property property">app</span><span class="token operator">:</span> LoadableApp<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span><span class="token punctuation">,</span>
  <span class="token literal-property property">configuration</span><span class="token operator">:</span> FrameworkConfiguration <span class="token operator">=</span> <span class="token punctuation">&#123;</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
  lifeCycles<span class="token operator">?</span><span class="token operator">:</span> FrameworkLifeCycles<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span><span class="token punctuation">,</span>
<span class="token punctuation">)</span><span class="token operator">:</span> Promise<span class="token operator">&lt;</span>ParcelConfigObject<span class="token operator">></span> <span class="token punctuation">&#123;</span>
  <span class="token comment">// 微应用的入口和名称</span>
  <span class="token keyword">const</span> <span class="token punctuation">&#123;</span> entry<span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> appName <span class="token punctuation">&#125;</span> <span class="token operator">=</span> app<span class="token punctuation">;</span>
  <span class="token comment">// 实例 id</span>
  <span class="token keyword">const</span> appInstanceId <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>appName<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">_</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span><span class="token operator">+</span><span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">_</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>Math<span class="token punctuation">.</span><span class="token function">floor</span><span class="token punctuation">(</span>Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>

  <span class="token comment">// 下面这个不用管，就是生成一个标记名称，然后使用该名称在浏览器性能缓冲器中设置一个时间戳，可以用来度量程序的执行时间，performance.mark、performance.measure</span>
  <span class="token keyword">const</span> markName <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[qiankun] App </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>appInstanceId<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string"> Loading</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">NODE_ENV</span> <span class="token operator">===</span> <span class="token string">'development'</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token function">performanceMark</span><span class="token punctuation">(</span>markName<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">&#125;</span>

  <span class="token comment">// 配置信息</span>
  <span class="token keyword">const</span> <span class="token punctuation">&#123;</span> singular <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">,</span> sandbox <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">,</span> excludeAssetFilter<span class="token punctuation">,</span> <span class="token operator">...</span>importEntryOpts <span class="token punctuation">&#125;</span> <span class="token operator">=</span> configuration<span class="token punctuation">;</span>

  <span class="token comment">/**
   * 获取微应用的入口 html 内容和脚本执行器
   * template 是 link 替换为 style 后的 template
   * execScript 是 让 JS 代码(scripts)在指定 上下文 中运行
   * assetPublicPath 是静态资源地址
   */</span>
  <span class="token keyword">const</span> <span class="token punctuation">&#123;</span> template<span class="token punctuation">,</span> execScripts<span class="token punctuation">,</span> assetPublicPath <span class="token punctuation">&#125;</span> <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">importEntry</span><span class="token punctuation">(</span>entry<span class="token punctuation">,</span> importEntryOpts<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// single-spa 的限制，加载、初始化和卸载不能同时进行，必须等卸载完成以后才可以进行加载，这个 promise 会在微应用卸载完成后被 resolve，在后面可以看到</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">await</span> <span class="token function">validateSingularMode</span><span class="token punctuation">(</span>singular<span class="token punctuation">,</span> app<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">await</span> <span class="token punctuation">(</span>prevAppUnmountedDeferred <span class="token operator">&amp;&amp;</span> prevAppUnmountedDeferred<span class="token punctuation">.</span>promise<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">&#125;</span>

  <span class="token comment">// --------------- 样式隔离 ---------------</span>
  <span class="token comment">// 是否严格样式隔离</span>
  <span class="token keyword">const</span> strictStyleIsolation <span class="token operator">=</span> <span class="token keyword">typeof</span> sandbox <span class="token operator">===</span> <span class="token string">'object'</span> <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span><span class="token operator">!</span>sandbox<span class="token punctuation">.</span>strictStyleIsolation<span class="token punctuation">;</span>
  <span class="token comment">// 实验性的样式隔离，后面就叫 scoped css，和严格样式隔离不能同时开启，如果开启了严格样式隔离，则 scoped css 就为 false，强制关闭</span>
  <span class="token keyword">const</span> enableScopedCSS <span class="token operator">=</span> <span class="token function">isEnableScopedCSS</span><span class="token punctuation">(</span>configuration<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// 用一个容器元素包裹微应用入口 html 模版, appContent = `&lt;div id="__qiankun_microapp_wrapper_for_$&#123;appInstanceId&#125;__" data-name="$&#123;appName&#125;">$&#123;template&#125;&lt;/div>`</span>
  <span class="token keyword">const</span> appContent <span class="token operator">=</span> <span class="token function">getDefaultTplWrapper</span><span class="token punctuation">(</span>appInstanceId<span class="token punctuation">,</span> appName<span class="token punctuation">)</span><span class="token punctuation">(</span>template<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 将 appContent 有字符串模版转换为 html dom 元素，如果需要开启样式严格隔离，则将 appContent 的子元素即微应用入口模版用 shadow dom 包裹起来，以达到样式严格隔离的目的</span>
  <span class="token keyword">let</span> <span class="token literal-property property">element</span><span class="token operator">:</span> HTMLElement <span class="token operator">|</span> <span class="token keyword">null</span> <span class="token operator">=</span> <span class="token function">createElement</span><span class="token punctuation">(</span>appContent<span class="token punctuation">,</span> strictStyleIsolation<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 通过 scoped css 的方式隔离样式，从这里也就能看出官方为什么说：</span>
  <span class="token comment">// 在目前的阶段，该功能还不支持动态的、使用 &lt;link />标签来插入外联的样式，但考虑在未来支持这部分场景</span>
  <span class="token comment">// 在现阶段只处理 style 这种内联标签的情况</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>element <span class="token operator">&amp;&amp;</span> <span class="token function">isEnableScopedCSS</span><span class="token punctuation">(</span>configuration<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">const</span> styleNodes <span class="token operator">=</span> element<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'style'</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token function">forEach</span><span class="token punctuation">(</span>styleNodes<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">stylesheetElement</span><span class="token operator">:</span> HTMLStyleElement</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
      css<span class="token punctuation">.</span><span class="token function">process</span><span class="token punctuation">(</span>element<span class="token operator">!</span><span class="token punctuation">,</span> stylesheetElement<span class="token punctuation">,</span> appName<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">&#125;</span>

  <span class="token comment">// --------------- 渲染微应用 ---------------</span>
  <span class="token comment">// 主应用装载微应用的容器节点</span>
  <span class="token keyword">const</span> container <span class="token operator">=</span> <span class="token string">'container'</span> <span class="token keyword">in</span> app <span class="token operator">?</span> app<span class="token punctuation">.</span>container <span class="token operator">:</span> <span class="token keyword">undefined</span><span class="token punctuation">;</span>
  <span class="token comment">// 这个是 1.x 版本遗留下来的实现，如果提供了 render 函数，当微应用需要被激活时就执行 render 函数渲染微应用，新版本用的 container，弃了 render</span>
  <span class="token comment">// 而且 legacyRender 和 strictStyleIsolation、scoped css 不兼容</span>
  <span class="token keyword">const</span> legacyRender <span class="token operator">=</span> <span class="token string">'render'</span> <span class="token keyword">in</span> app <span class="token operator">?</span> app<span class="token punctuation">.</span>render <span class="token operator">:</span> <span class="token keyword">undefined</span><span class="token punctuation">;</span>

  <span class="token comment">// 返回一个 render 函数，这个 render 函数要不使用用户传递的 render 函数，要不将 element 插入到 container</span>
  <span class="token keyword">const</span> render <span class="token operator">=</span> <span class="token function">getRender</span><span class="token punctuation">(</span>appName<span class="token punctuation">,</span> appContent<span class="token punctuation">,</span> container<span class="token punctuation">,</span> legacyRender<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// 渲染微应用到容器节点，并显示 loading 状态</span>
  <span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span> element<span class="token punctuation">,</span> <span class="token literal-property property">loading</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">&#125;</span><span class="token punctuation">,</span> <span class="token string">'loading'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// 得到一个 getter 函数，通过该函数可以获取 &lt;div id="__qiankun_microapp_wrapper_for_$&#123;appInstanceId&#125;__" data-name="$&#123;appName&#125;">$&#123;template&#125;&lt;/div></span>
  <span class="token keyword">const</span> containerGetter <span class="token operator">=</span> <span class="token function">getAppWrapperGetter</span><span class="token punctuation">(</span>
    appName<span class="token punctuation">,</span>
    appInstanceId<span class="token punctuation">,</span>
    <span class="token operator">!</span><span class="token operator">!</span>legacyRender<span class="token punctuation">,</span>
    strictStyleIsolation<span class="token punctuation">,</span>
    enableScopedCSS<span class="token punctuation">,</span>
    <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> element<span class="token punctuation">,</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// --------------- 运行时沙箱 ---------------</span>
  <span class="token comment">// 保证每一个微应用运行在一个干净的环境中（JS 执行上下文独立、应用间不会发生样式污染）</span>
  <span class="token keyword">let</span> global <span class="token operator">=</span> window<span class="token punctuation">;</span>
  <span class="token keyword">let</span> <span class="token function-variable function">mountSandbox</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> Promise<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">let</span> <span class="token function-variable function">unmountSandbox</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> Promise<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>sandbox<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">/**
     * 生成运行时沙箱，这个沙箱其实由两部分组成 => JS 沙箱（执行上下文）、样式沙箱
     *
     * 沙箱返回 window 的代理对象 proxy 和 mount、unmount 两个方法
     * unmount 方法会让微应用失活，恢复被增强的原生方法，并记录一堆 rebuild 函数，这个函数是微应用卸载时希望自己被重新挂载时要做的一些事情，比如动态样式表重建（卸载时会缓存）
     * mount 方法会执行一些一些 patch 动作，恢复原生方法的增强功能，并执行 rebuild 函数，将微应用恢复到卸载时的状态，当然从初始化状态进入挂载状态就没有恢复一说了
     */</span>
    <span class="token keyword">const</span> sandboxInstance <span class="token operator">=</span> <span class="token function">createSandbox</span><span class="token punctuation">(</span>
      appName<span class="token punctuation">,</span>
      containerGetter<span class="token punctuation">,</span>
      <span class="token function">Boolean</span><span class="token punctuation">(</span>singular<span class="token punctuation">)</span><span class="token punctuation">,</span>
      enableScopedCSS<span class="token punctuation">,</span>
      excludeAssetFilter<span class="token punctuation">,</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 用沙箱的代理对象作为接下来使用的全局对象</span>
    global <span class="token operator">=</span> sandboxInstance<span class="token punctuation">.</span>proxy <span class="token keyword">as</span> <span class="token keyword">typeof</span> window<span class="token punctuation">;</span>
    mountSandbox <span class="token operator">=</span> sandboxInstance<span class="token punctuation">.</span>mount<span class="token punctuation">;</span>
    unmountSandbox <span class="token operator">=</span> sandboxInstance<span class="token punctuation">.</span>unmount<span class="token punctuation">;</span>
  <span class="token punctuation">&#125;</span>

  <span class="token comment">// 合并用户传递的生命周期对象和 qiankun 框架内置的生命周期对象</span>
  <span class="token keyword">const</span> <span class="token punctuation">&#123;</span> beforeUnmount <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> afterUnmount <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> afterMount <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> beforeMount <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> beforeLoad <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">&#125;</span> <span class="token operator">=</span> <span class="token function">mergeWith</span><span class="token punctuation">(</span>
    <span class="token punctuation">&#123;</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
    <span class="token comment">// 返回内置生命周期对象，global.__POWERED_BY_QIANKUN__ 和 global.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ 的设置就是在内置的生命周期对象中设置的</span>
    <span class="token function">getAddOns</span><span class="token punctuation">(</span>global<span class="token punctuation">,</span> assetPublicPath<span class="token punctuation">)</span><span class="token punctuation">,</span>
    lifeCycles<span class="token punctuation">,</span>
    <span class="token punctuation">(</span><span class="token parameter">v1<span class="token punctuation">,</span> v2</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">concat</span><span class="token punctuation">(</span>v1 <span class="token operator">??</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> v2 <span class="token operator">??</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">await</span> <span class="token function">execHooksChain</span><span class="token punctuation">(</span><span class="token function">toArray</span><span class="token punctuation">(</span>beforeLoad<span class="token punctuation">)</span><span class="token punctuation">,</span> app<span class="token punctuation">,</span> global<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// get the lifecycle hooks from module exports，获取微应用暴露出来的生命周期函数</span>
  <span class="token keyword">const</span> <span class="token literal-property property">scriptExports</span><span class="token operator">:</span> any <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">execScripts</span><span class="token punctuation">(</span>global<span class="token punctuation">,</span> <span class="token operator">!</span>singular<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token punctuation">&#123;</span> bootstrap<span class="token punctuation">,</span> mount<span class="token punctuation">,</span> unmount<span class="token punctuation">,</span> update <span class="token punctuation">&#125;</span> <span class="token operator">=</span> <span class="token function">getLifecyclesFromExports</span><span class="token punctuation">(</span>scriptExports<span class="token punctuation">,</span> appName<span class="token punctuation">,</span> global<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// 给微应用注册通信方法并返回通信方法，然后会将通信方法通过 props 注入到微应用</span>
  <span class="token keyword">const</span> <span class="token punctuation">&#123;</span>
    onGlobalStateChange<span class="token punctuation">,</span>
    setGlobalState<span class="token punctuation">,</span>
    offGlobalStateChange<span class="token punctuation">,</span>
  <span class="token punctuation">&#125;</span><span class="token operator">:</span> Record<span class="token operator">&lt;</span>string<span class="token punctuation">,</span> Function<span class="token operator">></span> <span class="token operator">=</span> <span class="token function">getMicroAppStateActions</span><span class="token punctuation">(</span>appInstanceId<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">const</span> <span class="token literal-property property">parcelConfig</span><span class="token operator">:</span> ParcelConfigObject <span class="token operator">=</span> <span class="token punctuation">&#123;</span>
    <span class="token literal-property property">name</span><span class="token operator">:</span> appInstanceId<span class="token punctuation">,</span>
    bootstrap<span class="token punctuation">,</span>
    <span class="token comment">// 挂载阶段需要执行的一系列方法</span>
    <span class="token literal-property property">mount</span><span class="token operator">:</span> <span class="token punctuation">[</span>
      <span class="token comment">// 性能度量，不用管</span>
      <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">NODE_ENV</span> <span class="token operator">===</span> <span class="token string">'development'</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
          <span class="token keyword">const</span> marks <span class="token operator">=</span> performance<span class="token punctuation">.</span><span class="token function">getEntriesByName</span><span class="token punctuation">(</span>markName<span class="token punctuation">,</span> <span class="token string">'mark'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
          <span class="token comment">// mark length is zero means the app is remounting</span>
          <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>marks<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token function">performanceMark</span><span class="token punctuation">(</span>markName<span class="token punctuation">)</span><span class="token punctuation">;</span>
          <span class="token punctuation">&#125;</span>
        <span class="token punctuation">&#125;</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
      <span class="token comment">// 单例模式需要等微应用卸载完成以后才能执行挂载任务，promise 会在微应用卸载完以后 resolve</span>
      <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">await</span> <span class="token function">validateSingularMode</span><span class="token punctuation">(</span>singular<span class="token punctuation">,</span> app<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> prevAppUnmountedDeferred<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
          <span class="token keyword">return</span> prevAppUnmountedDeferred<span class="token punctuation">.</span>promise<span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>

        <span class="token keyword">return</span> <span class="token keyword">undefined</span><span class="token punctuation">;</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
      <span class="token comment">// 添加 mount hook, 确保每次应用加载前容器 dom 结构已经设置完毕</span>
      <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
        <span class="token comment">// element would be destroyed after unmounted, we need to recreate it if it not exist</span>
        <span class="token comment">// unmount 阶段会置空，这里重新生成</span>
        element <span class="token operator">=</span> element <span class="token operator">||</span> <span class="token function">createElement</span><span class="token punctuation">(</span>appContent<span class="token punctuation">,</span> strictStyleIsolation<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 渲染微应用到容器节点，并显示 loading 状态</span>
        <span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span> element<span class="token punctuation">,</span> <span class="token literal-property property">loading</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">&#125;</span><span class="token punctuation">,</span> <span class="token string">'mounting'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
      <span class="token comment">// 运行时沙箱导出的 mount</span>
      mountSandbox<span class="token punctuation">,</span>
      <span class="token comment">// exec the chain after rendering to keep the behavior with beforeLoad</span>
      <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">execHooksChain</span><span class="token punctuation">(</span><span class="token function">toArray</span><span class="token punctuation">(</span>beforeMount<span class="token punctuation">)</span><span class="token punctuation">,</span> app<span class="token punctuation">,</span> global<span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token comment">// 向微应用的 mount 生命周期函数传递参数，比如微应用中使用的 props.onGlobalStateChange 方法</span>
      <span class="token keyword">async</span> <span class="token parameter">props</span> <span class="token operator">=></span> <span class="token function">mount</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span> <span class="token operator">...</span>props<span class="token punctuation">,</span> <span class="token literal-property property">container</span><span class="token operator">:</span> <span class="token function">containerGetter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> setGlobalState<span class="token punctuation">,</span> onGlobalStateChange <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token comment">// 应用 mount 完成后结束 loading</span>
      <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span> element<span class="token punctuation">,</span> <span class="token literal-property property">loading</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">&#125;</span><span class="token punctuation">,</span> <span class="token string">'mounted'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">execHooksChain</span><span class="token punctuation">(</span><span class="token function">toArray</span><span class="token punctuation">(</span>afterMount<span class="token punctuation">)</span><span class="token punctuation">,</span> app<span class="token punctuation">,</span> global<span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token comment">// initialize the unmount defer after app mounted and resolve the defer after it unmounted</span>
      <span class="token comment">// 微应用挂载完成以后初始化这个 promise，并且在微应用卸载以后 resolve 这个 promise</span>
      <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">await</span> <span class="token function">validateSingularMode</span><span class="token punctuation">(</span>singular<span class="token punctuation">,</span> app<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
          prevAppUnmountedDeferred <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Deferred</span><span class="token operator">&lt;</span><span class="token keyword">void</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
      <span class="token comment">// 性能度量，不用管</span>
      <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">NODE_ENV</span> <span class="token operator">===</span> <span class="token string">'development'</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
          <span class="token keyword">const</span> measureName <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">[qiankun] App </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>appInstanceId<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string"> Loading Consuming</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
          <span class="token function">performanceMeasure</span><span class="token punctuation">(</span>measureName<span class="token punctuation">,</span> markName<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
    <span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token comment">// 卸载微应用</span>
    <span class="token literal-property property">unmount</span><span class="token operator">:</span> <span class="token punctuation">[</span>
      <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">execHooksChain</span><span class="token punctuation">(</span><span class="token function">toArray</span><span class="token punctuation">(</span>beforeUnmount<span class="token punctuation">)</span><span class="token punctuation">,</span> app<span class="token punctuation">,</span> global<span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token comment">// 执行微应用的 unmount 生命周期函数</span>
      <span class="token keyword">async</span> <span class="token parameter">props</span> <span class="token operator">=></span> <span class="token function">unmount</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span> <span class="token operator">...</span>props<span class="token punctuation">,</span> <span class="token literal-property property">container</span><span class="token operator">:</span> <span class="token function">containerGetter</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token comment">// 沙箱导出的 unmount 方法</span>
      unmountSandbox<span class="token punctuation">,</span>
      <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">execHooksChain</span><span class="token punctuation">(</span><span class="token function">toArray</span><span class="token punctuation">(</span>afterUnmount<span class="token punctuation">)</span><span class="token punctuation">,</span> app<span class="token punctuation">,</span> global<span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token comment">// 显示 loading 状态、移除微应用的状态监听、置空 element</span>
      <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
        <span class="token function">render</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span> <span class="token literal-property property">element</span><span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token literal-property property">loading</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">&#125;</span><span class="token punctuation">,</span> <span class="token string">'unmounted'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">offGlobalStateChange</span><span class="token punctuation">(</span>appInstanceId<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// for gc</span>
        element <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
      <span class="token comment">// 微应用卸载以后 resolve 这个 promise，框架就可以进行后续的工作，比如加载或者挂载其它微应用</span>
      <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">await</span> <span class="token function">validateSingularMode</span><span class="token punctuation">(</span>singular<span class="token punctuation">,</span> app<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> prevAppUnmountedDeferred<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
          prevAppUnmountedDeferred<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
      <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
    <span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token punctuation">&#125;</span><span class="token punctuation">;</span>

  <span class="token comment">// 微应用有可能定义 update 方法</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> update <span class="token operator">===</span> <span class="token string">'function'</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    parcelConfig<span class="token punctuation">.</span>update <span class="token operator">=</span> update<span class="token punctuation">;</span>
  <span class="token punctuation">&#125;</span>

  <span class="token keyword">return</span> parcelConfig<span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h4 id="样式隔离"><a href="#样式隔离" class="headerlink" title="样式隔离"></a>样式隔离</h4><p>qiankun 的样式隔离有两种方式，一种是严格样式隔离，通过 shadow dom 来实现，另一种是实验性的样式隔离，就是 scoped css，两种方式不可共存</p>
<h4 id="严格样式隔离"><a href="#严格样式隔离" class="headerlink" title="严格样式隔离"></a>严格样式隔离</h4><p>在 qiankun 中的严格样式隔离，就是在这个 createElement 方法中做的，通过 shadow dom 来实现， shadow dom 是浏览器原生提供的一种能力，在过去的很长一段时间里，浏览器用它来封装一些元素的内部结构。以一个有着默认播放控制按钮的 <video> 元素为例，实际上，在它的 Shadow DOM 中，包含来一系列的按钮和其他控制器。Shadow DOM 标准允许你为你自己的元素（custom element）维护一组 Shadow DOM。具体内容可查看 shadow DOM</p>
<pre class="line-numbers language-javascript" data-language="javascript"><code class="language-javascript"><span class="token comment">/**
 * 做了两件事
 *  1、将 appContent 由字符串模版转换成 html dom 元素
 *  2、如果需要开启严格样式隔离，则将 appContent 的子元素即微应用的入口模版用 shadow dom 包裹起来，达到样式严格隔离的目的
 * @param appContent = `&lt;div id="__qiankun_microapp_wrapper_for_$&#123;appInstanceId&#125;__" data-name="$&#123;appName&#125;">$&#123;template&#125;&lt;/div>`
 * @param strictStyleIsolation 是否开启严格样式隔离
 */</span>
<span class="token keyword">function</span> <span class="token function">createElement</span><span class="token punctuation">(</span><span class="token parameter"><span class="token literal-property property">appContent</span><span class="token operator">:</span> string<span class="token punctuation">,</span> <span class="token literal-property property">strictStyleIsolation</span><span class="token operator">:</span> boolean</span><span class="token punctuation">)</span><span class="token operator">:</span> HTMLElement <span class="token punctuation">&#123;</span>
  <span class="token comment">// 创建一个 div 元素</span>
  <span class="token keyword">const</span> containerElement <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">'div'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 将字符串模版 appContent 设置为 div 的子与阿苏</span>
  containerElement<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> appContent<span class="token punctuation">;</span>
  <span class="token comment">// appContent always wrapped with a singular div，appContent 由模版字符串变成了 DOM 元素</span>
  <span class="token keyword">const</span> appElement <span class="token operator">=</span> containerElement<span class="token punctuation">.</span>firstChild <span class="token keyword">as</span> HTMLElement<span class="token punctuation">;</span>
  <span class="token comment">// 如果开启了严格的样式隔离，则将 appContent 的子元素（微应用的入口模版）用 shadow dom 包裹，以达到微应用之间样式严格隔离的目的</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>strictStyleIsolation<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>supportShadowDOM<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
      console<span class="token punctuation">.</span><span class="token function">warn</span><span class="token punctuation">(</span>
        <span class="token string">'[qiankun]: As current browser not support shadow dom, your strictStyleIsolation configuration will be ignored!'</span><span class="token punctuation">,</span>
      <span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token punctuation">&#123;</span>
      <span class="token keyword">const</span> <span class="token punctuation">&#123;</span> innerHTML <span class="token punctuation">&#125;</span> <span class="token operator">=</span> appElement<span class="token punctuation">;</span>
      appElement<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token string">''</span><span class="token punctuation">;</span>
      <span class="token keyword">let</span> <span class="token literal-property property">shadow</span><span class="token operator">:</span> ShadowRoot<span class="token punctuation">;</span>

      <span class="token keyword">if</span> <span class="token punctuation">(</span>appElement<span class="token punctuation">.</span>attachShadow<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        shadow <span class="token operator">=</span> appElement<span class="token punctuation">.</span><span class="token function">attachShadow</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span> <span class="token literal-property property">mode</span><span class="token operator">:</span> <span class="token string">'open'</span> <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token punctuation">&#123;</span>
        <span class="token comment">// createShadowRoot was proposed in initial spec, which has then been deprecated</span>
        shadow <span class="token operator">=</span> <span class="token punctuation">(</span>appElement <span class="token keyword">as</span> any<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">createShadowRoot</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">&#125;</span>
      shadow<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> innerHTML<span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
  <span class="token punctuation">&#125;</span>

  <span class="token keyword">return</span> appElement<span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>以上内容就是对 qiankun 框架的完整解读了</p>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="http://cdn.pluto1811.com/pluto">拾壹baby</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="http://cdn.pluto1811.com/pluto/2022/09/02/qiankun02/">http://cdn.pluto1811.com/pluto/2022/09/02/qiankun02/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="http://cdn.pluto1811.com/pluto" target="_blank">Pluto</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"></div><div class="post_share"><div class="social-share" data-image="http://cdn.pluto1811.com/goodsImage/1615971759919/1615971739665.jpg" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/js/social-share.min.js" defer></script></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/pluto/2022/09/05/qiankun01/"><img class="prev-cover" src="http://cdn.pluto1811.com/goodsImage/1615966170678/1615966002577.jpg" onerror="onerror=null;src='/pluto/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">qiankun源码解读-示例篇</div></div></a></div><div class="next-post pull-right"><a href="/pluto/2022/08/10/babel01/"><img class="next-cover" src="http://cdn.pluto1811.com/goodsImage/1626858229328/1626858211842.jpg" onerror="onerror=null;src='/pluto/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">babel 探秘系列-基础篇</div></div></a></div></nav></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="is-center"><div class="avatar-img"><img src="https://i.loli.net/2021/02/24/5O1day2nriDzjSu.png" onerror="this.onerror=null;this.src='/pluto/img/friend_404.gif'" alt="avatar"/></div><div class="author-info__name">拾壹baby</div><div class="author-info__description">个人博客</div></div><div class="card-info-data site-data is-center"><a href="/pluto/archives/"><div class="headline">文章</div><div class="length-num">22</div></a><a href="/pluto/tags/"><div class="headline">标签</div><div class="length-num">0</div></a><a href="/pluto/categories/"><div class="headline">分类</div><div class="length-num">0</div></a></div><a id="card-info-btn" target="_blank" rel="noopener" href="https://github.com/xxxxxx"><i class="fab fa-github"></i><span>Follow Me</span></a></div><div class="card-widget card-announcement"><div class="item-headline"><i class="fas fa-bullhorn fa-shake"></i><span>公告</span></div><div class="announcement_content">This is my Blog</div></div><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#%E6%A1%86%E6%9E%B6%E6%BA%90%E7%A0%81"><span class="toc-number">1.</span> <span class="toc-text">框架源码</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%85%A5%E5%8F%A3-src-x2F-index-ts"><span class="toc-number">1.0.0.1.</span> <span class="toc-text">入口 src&#x2F;index.ts</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#registerMicroApps"><span class="toc-number">1.0.0.2.</span> <span class="toc-text">registerMicroApps</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#start"><span class="toc-number">1.0.0.3.</span> <span class="toc-text">start</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%A2%84%E5%8A%A0%E8%BD%BD-doPrefetchStrategy"><span class="toc-number">1.0.0.4.</span> <span class="toc-text">预加载 - doPrefetchStrategy</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%BA%94%E7%94%A8%E9%97%B4%E9%80%9A%E4%BF%A1-initGlobalState"><span class="toc-number">1.0.0.5.</span> <span class="toc-text">应用间通信 initGlobalState</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%85%A8%E5%B1%80%E6%9C%AA%E6%8D%95%E8%8E%B7%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E5%99%A8"><span class="toc-number">1.0.0.6.</span> <span class="toc-text">全局未捕获异常处理器</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#setDefaultMountApp"><span class="toc-number">1.0.0.7.</span> <span class="toc-text">setDefaultMountApp</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#runAfterFirstMounted"><span class="toc-number">1.0.0.8.</span> <span class="toc-text">runAfterFirstMounted</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%89%8B%E5%8A%A8%E5%8A%A0%E8%BD%BD%E5%BE%AE%E5%BA%94%E7%94%A8-loadMicroApp"><span class="toc-number">1.0.0.9.</span> <span class="toc-text">手动加载微应用 loadMicroApp</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#qiankun-%E7%9A%84%E6%A0%B8%E5%BF%83-loadApp"><span class="toc-number">1.0.0.10.</span> <span class="toc-text">qiankun 的核心 loadApp</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%A0%B7%E5%BC%8F%E9%9A%94%E7%A6%BB"><span class="toc-number">1.0.0.11.</span> <span class="toc-text">样式隔离</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%A5%E6%A0%BC%E6%A0%B7%E5%BC%8F%E9%9A%94%E7%A6%BB"><span class="toc-number">1.0.0.12.</span> <span class="toc-text">严格样式隔离</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BB%93%E8%AF%AD"><span class="toc-number">1.1.</span> <span class="toc-text">结语</span></a></li></ol></li></ol></div></div><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>最新文章</span></div><div class="aside-list"><div class="aside-list-item"><a class="thumbnail" href="/pluto/2022/10/25/me2022-10/" title="嗯，这两三年...2022年中总结"><img src="http://cdn.pluto1811.com/goodsImage/1650448154299/1650448126499.jpg" onerror="this.onerror=null;this.src='/pluto/img/404.jpg'" alt="嗯，这两三年...2022年中总结"/></a><div class="content"><a class="title" href="/pluto/2022/10/25/me2022-10/" title="嗯，这两三年...2022年中总结">嗯，这两三年...2022年中总结</a><time datetime="2022-10-25T05:20:55.000Z" title="发表于 2022-10-25 13:20:55">2022-10-25</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/pluto/2022/10/25/promise/" title="Promise里的代码为什么比setTimeout先执行？"><img src="http://cdn.pluto1811.com/goodsImage/1615188358809/376997778.jpg" onerror="this.onerror=null;this.src='/pluto/img/404.jpg'" alt="Promise里的代码为什么比setTimeout先执行？"/></a><div class="content"><a class="title" href="/pluto/2022/10/25/promise/" title="Promise里的代码为什么比setTimeout先执行？">Promise里的代码为什么比setTimeout先执行？</a><time datetime="2022-10-25T05:20:55.000Z" title="发表于 2022-10-25 13:20:55">2022-10-25</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/pluto/2022/10/23/jsobject/" title="JavaScript对象：啥？对象还有分类？"><img src="http://cdn.pluto1811.com/goodsImage/1615184906664/374751567.jpg" onerror="this.onerror=null;this.src='/pluto/img/404.jpg'" alt="JavaScript对象：啥？对象还有分类？"/></a><div class="content"><a class="title" href="/pluto/2022/10/23/jsobject/" title="JavaScript对象：啥？对象还有分类？">JavaScript对象：啥？对象还有分类？</a><time datetime="2022-10-23T05:20:55.000Z" title="发表于 2022-10-23 13:20:55">2022-10-23</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/pluto/2022/10/19/hexo-ci/" title="【超实用】完美实现基于hexo的博客网站自动化部署"><img src="http://cdn.pluto1811.com/20221020140348.png" onerror="this.onerror=null;this.src='/pluto/img/404.jpg'" alt="【超实用】完美实现基于hexo的博客网站自动化部署"/></a><div class="content"><a class="title" href="/pluto/2022/10/19/hexo-ci/" title="【超实用】完美实现基于hexo的博客网站自动化部署">【超实用】完美实现基于hexo的博客网站自动化部署</a><time datetime="2022-10-19T06:20:58.000Z" title="发表于 2022-10-19 14:20:58">2022-10-19</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/pluto/2022/10/16/jstyping/" title="JavaScript类型，有可能你不知道这些细节"><img src="http://cdn.pluto1811.com/20221020140348.png" onerror="this.onerror=null;this.src='/pluto/img/404.jpg'" alt="JavaScript类型，有可能你不知道这些细节"/></a><div class="content"><a class="title" href="/pluto/2022/10/16/jstyping/" title="JavaScript类型，有可能你不知道这些细节">JavaScript类型，有可能你不知道这些细节</a><time datetime="2022-10-16T05:20:55.000Z" title="发表于 2022-10-16 13:20:55">2022-10-16</time></div></div></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2020 - 2023 By 拾壹baby</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div><script src="/pluto/js/utils.js"></script><script src="/pluto/js/main.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.umd.min.js"></script><div class="js-pjax"></div><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div></body></html>